#include "stream_reassembler.hh"

// Dummy implementation of a stream reassembler.

// For Lab 1, please replace with a real implementation that passes the
// automated checks run by `make check_lab1`.

// You will need to add private members to the class declaration in `stream_reassembler.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

StreamReassembler::StreamReassembler(const size_t capacity)
    : _eof(false)
    , _next_index(0)
    , _buffer_size(0)
    , _capacity(capacity)
    , _output(capacity)
    , _buffer(capacity)
    , _exist_data(capacity, false) {}

//! \details This function accepts a substring (aka a segment) of bytes,
//! possibly out-of-order, from the logical stream, and assembles any newly
//! contiguous substrings and writes them into the output stream in order.
void StreamReassembler::push_substring(const string &data, const size_t index, const bool eof) {
    if (eof)
        _eof |= eof;
    /*   if (data.empty() || data.size() + index <= _next_index)
           return;*/
    // size_t length = min(data.size(), remaining_capacity());
    merge(data, index);
    string str = "";
    size_t tmp = _next_index % _capacity;
    while (_exist_data[tmp]) {
        str.push_back(_buffer[tmp]);
        _exist_data[tmp] = false;
        ++tmp;
        tmp %= _capacity;
    }
    size_t written = _output.write(move(str));
    _buffer_size -= written;
    _next_index = _next_index + written;
    if (_eof && _buffer_size == 0)
        _output.end_input();
}

void StreamReassembler::merge(const string &data, const size_t &index) {
    size_t data_size = data.size();
    size_t l = (index + data_size) - _next_index;
    if (l <= data_size) {
        for (size_t i = 0; i < l; i++) {
            if (!_exist_data[(_next_index + i) % _capacity]) {
                _buffer[(_next_index + i) % _capacity] = data[data_size - l + i];
                _exist_data[(_next_index + i) % _capacity] = true;
                ++_buffer_size;
            }
        }
    } else {
        for (size_t i = 0; i < data_size; i++) {
            if (!_exist_data[(index + i) % _capacity]) {
                _buffer[(index + i) % _capacity] = data[i];
                _exist_data[(index + i) % _capacity] = true;
                ++_buffer_size;
            }
        }
    }
    // auto iter = _buffer.lower_bound(index);
    // if (iter == _buffer.begin() && iter != _buffer.end()) {
    //    if (index + data.size() < iter->first) {
    //        _buffer[index] = data;
    //        _buffer_size += data.size();
    //    } else {
    //        string str = iter->second.substr((index + data.size() - iter->first), string::npos);
    //        _buffer[index] = data + str;
    //        _buffer_size += str.size() + data.size() - iter->second.size();
    //        _buffer.erase(iter);
    //    }
    //} else {
    //    if (_buffer.empty()) {
    //        if (data.size() > _next_index - index) {
    //            size_t l = data.size() - (_next_index - index);
    //            string str = data.substr(data.size() - l, string::npos);
    //            _buffer[_next_index] = str;
    //            _buffer_size += l;
    //        }
    //    } else {
    //        --iter;
    //        if (iter->first + iter->second.size() < index) {
    //            _buffer[index] = data;
    //            _buffer_size += data.size();
    //        } else {
    //            string str = data.substr(iter->first + data.size() - index, string::npos);
    //            _buffer[iter->first] = iter->second + str;
    //            _buffer_size += str.size();
    //        }
    //    }
    //}
}

size_t StreamReassembler::unassembled_bytes() const { return _buffer_size; }

bool StreamReassembler::empty() const { return _buffer_size == 0; }

// size_t StreamReassembler::remaining_capacity() const { return _capacity - _output.buffer_size() - _buffer_size; }